package model;

import java.util.ArrayList;

public class Matrix implements MatrixInterface
{

	private int dots;
	private int [][] matrix;
	public Matrix(int dots)
	{
		setDots(dots);
		matrix = new int[dots][dots];
	}
	
	private void setDots(int dots)
	{
		if (dots < 0) throw new IllegalArgumentException("Dots must be positive");
		this.dots = dots;
		
	}
	
	
	@Override
	public void setElement(int row, int col, int value)
	{
		if (row < 0 || col < 0) throw new IllegalArgumentException("Rows and Cols must be positive");
		if (value < 0 || value > 1) throw new IllegalArgumentException("value must be within 1 and 0");
		matrix[row-1][col-1] = value;
	}

	@Override
	public int getElement(int row, int col)
	{
		if(row <= 0 || col <= 0) throw new IllegalArgumentException("Rows and Cols must be positive");
		
		return matrix[row-1][col-1];
		
	}
	
	public int getDim()
	{
		return matrix.length; 
	}

	public int[][] getMatrix()
	{
		return matrix;
	}
	
	
	public int[][] getMatrix(int[][] a)
	{
		return a;
	}
	

	@Override
	public int getRowTotal(int row)
	{
		int total = 0;
		if (row < 0) throw new IllegalArgumentException("Row must be positive");
		
		for(int i = 0; i < dots; i++)
		{
			total += matrix[row-1][i];
		}
		
		return total;
	}

	@Override
	public int getColumnTotal(int col)
	{
	int total = 0;
	
	if(col < 0) throw new IllegalArgumentException("Column must be positive");
	
	for(int i = 0; i < dots; i++)
	{
		total += matrix[i][col-1];
	}
	
	return total;
	}

	public int[][] multiplyMatrices(int[][]a, int[][]b)
	{
		int[][]multiplymatrix = new int[dots][dots];
		
		for (int i = 0; i < dots; i++) 
		{
            for (int j = 0; j < dots; j++) 
            {
                for (int k = 0; k < dots; k++) 
                {
                    multiplymatrix[i][j]  += a[i][k] * b[k][j];
                }
            }
		}
		return multiplymatrix;
	}

	public int[][] getPower(int n)
	{		
		int [][] temp = new int[dots][dots];
		if(n < 0) throw new IllegalArgumentException("power must be greater than 0");
		if(n == 1) return matrix;
		temp = getMatrix(matrix);

	
		for(int i = 1; i < n; i++) 
		{
			temp = multiplyMatrices(matrix, temp);
		}
		return temp;
		
	}

	public int[][] calculatePathMatrix(int[][] matrix)
	{
		int[][]pmatrix = copy(matrix);
		int tmatrix[][] = copy(matrix);
		
		
			for(int i = 0; i < dots; i++)
			{
				tmatrix[i][i] = 1;
			}
			
			for(int p = 0; p < dots; p++)
			{
				pmatrix = multiplyMatrices(tmatrix, pmatrix);
				
				for(int i = 0; i < dots; i++)
				{
					for(int j = 0; j < dots; j++)
					{
						if(pmatrix[i][j] > 0)
						{
							tmatrix[i][j] = 1;
						}
					}
				}
			}
			return tmatrix;
	}

	public int[][] calculateDistanceMatrix(int[][] matrix)
	{
		int[][] distancematrix = new int[dots][dots];
		
		for(int i = 0; i < dots; i++)
		{
			for(int j = 0; j < dots; j++)
			{
				if(i == j)
				{
					distancematrix[i][j] = 0;
				}
				else
				{
					distancematrix[i][j] = -1;
				}
			}
		}
		
		for(int p = 1; p < matrix.length; p++)
		{
			matrix = getPower(p);
						
			for(int i = 0; i < matrix.length; i++)
			{
				for(int j = 0; j < matrix.length; j++)
				{
					if(matrix[i][j] > 0 && distancematrix[i][j] == -1)
					{
						distancematrix[i][j] = p;
					}
				}
			}
		}
		return distancematrix;
	}
	
	public int[] calculateEccentricity()
	{
		int[][]dmatrix = calculateDistanceMatrix(matrix);
		int[]eccentricity = new int[dmatrix.length];
		
		for(int i = 0; i < dmatrix.length; i++)
		{
			for(int j = 0; j < dmatrix.length;j++)
			{
				if(eccentricity[i] < dmatrix[i][j])
				{
					eccentricity[i] = dmatrix[i][j];
				}
			}
		}
		return eccentricity;		
	}
	
	public int[] calculateCenter()
	{
		
		return null;
	}
	
	
	
	public boolean isConnected(int[][] pmatrix, int dim)
	{
		for(int i = 0; i < dim; i++)
		{
			for(int j = 0; j < dim; j++)
			{
				if(pmatrix[i][j] != 1)
				{
					return false;
				}
			}
		}
		return true;
	}
	
	
	public ArrayList<ArrayList<Integer>> calculateComponents(int[][] pathmatrix)
	{
		ArrayList<ArrayList<Integer>> components = new ArrayList<ArrayList<Integer>>();
		
		for(int i = 0; i < pathmatrix.length; i++)
		{			
			ArrayList<Integer> component = new ArrayList<Integer>();
			for(int j = 0; j < pathmatrix.length; j++)
			{
				if(pathmatrix[i][j] == 1)
				{
					boolean alreadyadded = false;
				
					for(ArrayList<Integer> c : components)
						{
							if(c.contains(j+1))
							{
								alreadyadded = true;
							}
						}
					
					if(!alreadyadded)
					{
						component.add(j+1);
					}
				}
			}
			
			if(!(component.isEmpty()))
			{				
				components.add(component);
			}
		}		
		return components;
	}
	
	
	
	public ArrayList<Integer> calculateArticulations(ArrayList<ArrayList<Integer>> components, int[][] amatrix)
	{
		
		int numberOfComponents = components.size();
		ArrayList<Integer> articulations = new ArrayList<Integer>();
		
		for(int i = 0; i < amatrix.length; i++)
		{
			int[][] copy = copy(amatrix);
			
			for(int j = 0; j< copy.length; j++)
			{
				copy[i][j] = 0;
				copy[j][i] = 0;
			}
			
			int[][] pathmatrix = calculatePathMatrix(copy);
			ArrayList<ArrayList<Integer>> componentsmatrix = calculateComponents(pathmatrix);
			
			ArrayList<Integer> knots = new ArrayList<Integer>();
			knots.add(i + 1);
			componentsmatrix.remove(knots);
			
			if(componentsmatrix.size() > numberOfComponents)
			{
				articulations.add(i + 1);
			}
		}
		return articulations;
	}
	
	public ArrayList<ArrayList<Integer>>calculateBridges(ArrayList<ArrayList<Integer>> components, int[][] amatrix)
	{
		ArrayList<ArrayList<Integer>> bridges = new ArrayList<ArrayList<Integer>>();
		int numberOfComponents = components.size();
		int[][] copy = copy(amatrix);
		
		for(int i = 0; i < copy.length; i++)
		{
			for(int j = 0; j < copy.length; j++)
			{
				if(copy[i][j] == 1)
				{
					copy[i][j] = 0;
					copy[j][i] = 0;
					int[][] pathmatrix = calculatePathMatrix(copy);
					ArrayList<ArrayList<Integer>> componentsmatrix = calculateComponents(pathmatrix);
				
				
				if(componentsmatrix.size() > numberOfComponents)
				{
					ArrayList<Integer> bridge = new ArrayList<Integer>();
					
					bridge.add(Math.min(j + 1, i + 1));
					bridge.add(Math.max(j + 1, i + 1));
					
					if (!bridges.contains(bridge)) 
					{
						bridges.add(bridge);
					}
					
				}
				copy[i][j] = 1;
				copy[j][i] = 1;
				}
			}
		}
		return bridges;		
	}
	
	
	
	public void copyMatrices(int[][] outsidematrix)
	{
		for(int i = 0; i < dots; i++)
		{
			for(int j = 0; j < dots; j++)
			{
				matrix[i][j] = outsidematrix[i][j];
			}
		}	
	}
	
	
	public int[][] copy(int[][] outsidematrix)
	{
		int[][] insidematrix = new int[outsidematrix.length][outsidematrix.length];
		for(int i = 0; i < dots; i++)
		{
			for(int j = 0; j < dots; j++)
			{
				insidematrix[i][j] = outsidematrix[i][j];
			}
		}	
		return insidematrix;
	}
	
	
	public void printArray(int[][] array)
	{
		for(int i = 0; i < dots; i++)
		{
			System.out.println();
			
			for(int j = 0; j < dots; j++)
			{
				System.out.print(array[i][j]);
				System.out.print(" ");
			}
		}
	}
}
